using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;
using Konst3d.Efficiency.IO;

// LAYOUT:
// signature    4b (uint)    0
// version      4b (uint)    4
// count        4b (int)     8
// reserved   400b (...)    12
// <entries>       (...)   412

// ENTRY LAYOUT:
// created year     4b (int)     0   // if (year == -1) => cell is free
// created month    1b (byte)    4
// created day      1b (byte)    5
// target year      4b (int)     6
// target month     1b (byte)   10
// target day       1b (byte)   11
// title ID         4b (int)    12
// descr ID         4b (int)    16

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class CountDownTimersFile : UnorderedListFile<CountDownTimersFile.Entry>
    {
        #region Constants

        private const uint SIGNATURE = 0x57575757;
        private const uint VERSION = 1;
        private const int OFFSET_CAPACITY = 8;
        private const int OFFSET_DATA = 412;
        private const int OFFSET_ENTRY_CREATED_DATE = 0;
        private const int OFFSET_ENTRY_TARGET_DATE = 6;
        private const int OFFSET_ENTRY_TITLE_ID = 12;
        private const int OFFSET_ENTRY_DESCR_ID = 16;
        private const int ENTRY_BYTE_SIZE = 20;

        #endregion // Constants

        #region Types

        public struct Entry
        {
            public int TitleID;
            public int DescriptionID;
            public ShortDate TargetDate;
            public ShortDate CreatedDate;
        }

        #endregion // Types

        #region Create/Open Methods

        public static CountDownTimersFile Create(string path, bool overwrite)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));

            if (File.Exists(path) && !overwrite)
            {
                throw new IOException(String.Format("File \"{0}\" is already exists.", path));
            }

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, Encoding.UTF8);
                writer = new BinaryWriter(stream, Encoding.UTF8);

                writer.Write(SIGNATURE);
                writer.Write(VERSION);
                writer.Write((int)0);
                writer.WriteBytes(0, 400);

                writer.Flush();
                writer.Seek(0, SeekOrigin.Begin);

                return new CountDownTimersFile(stream, reader, writer);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        public static CountDownTimersFile Open(string path)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(File.Exists(path));

            FileStream stream = null;
            BinaryReader reader = null;
            BinaryWriter writer = null;

            try
            {
                stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
                reader = new BinaryReader(stream, Encoding.UTF8);
                writer = new BinaryWriter(stream, Encoding.UTF8);

                return new CountDownTimersFile(stream, reader, writer);
            }
            catch
            {
                if (stream != null)
                {
                    stream.Dispose();
                }

                if (reader != null)
                {
                    reader.Dispose();
                }

                if (writer != null)
                {
                    writer.Dispose();
                }

                throw;
            }
        }

        #endregion // Create/Open Methods

        #region Constructor

        private CountDownTimersFile(FileStream file, BinaryReader reader, BinaryWriter writer):
            base(file, reader, writer, SIGNATURE, VERSION)
        {
        }

        #endregion // Constructor

        #region UnorderedListFile<CountDownTimersFile2.Entry> Members

        protected override long CapacityOffset
        {
            get
            {
                return OFFSET_CAPACITY;
            }
        }

        protected override long DataOffset
        {
            get
            {
                return OFFSET_DATA;
            }
        }

        protected override int EntryByteSize
        {
            get
            {
                return ENTRY_BYTE_SIZE;
            }
        }

        protected override CountDownTimersFile.Entry ReadData(long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                _reader.BaseStream.Seek(offset, origin);
            }

            var entry = new Entry();

            entry.CreatedDate = _reader.ReadShortDate();
            entry.TargetDate = _reader.ReadShortDate();
            entry.TitleID = _reader.ReadInt32();
            entry.DescriptionID = _reader.ReadInt32();

            return entry;
        }

        protected override void WriteData(CountDownTimersFile.Entry entry, long offset = -1, SeekOrigin origin = SeekOrigin.Begin)
        {
            if (offset >= 0)
            {
                _writer.Seek((int)offset, origin);
            }

            _writer.Write(entry.CreatedDate);
            _writer.Write(entry.TargetDate);
            _writer.Write(entry.TitleID);
            _writer.Write(entry.DescriptionID);

            _writer.Flush();
        }

        #endregion // UnorderedListFile<CountDownTimersFile2.Entry> Members

        #region Public Methods

        public int AddEntry(int titleID, int descrID, DateTime target, DateTime created)
        {
            return AddEntry(new Entry() { CreatedDate = new ShortDate(created), TargetDate = new ShortDate(target), TitleID = titleID, DescriptionID = descrID });
        }

        public int GetEntryTitleID(int id)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            return _reader.ReadInt32(GetEntryDataOffset(id) + OFFSET_ENTRY_TITLE_ID);
        }

        public void SetEntryTitleID(int id, int titleID)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            _writer.Write(titleID, (int)GetEntryDataOffset(id) + OFFSET_ENTRY_TITLE_ID);
            _writer.Flush();
        }

        public int GetEntryDescrID(int id)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            return _reader.ReadInt32(GetEntryDataOffset(id) + OFFSET_ENTRY_DESCR_ID);
        }

        public void SetEntryDescrID(int id, int descrID)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            _writer.Write(descrID, (int)GetEntryDataOffset(id) + OFFSET_ENTRY_DESCR_ID);
            _writer.Flush();
        }

        public DateTime GetEntryTargetDate(int id)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            return _reader.ReadShortDate(GetEntryDataOffset(id) + OFFSET_ENTRY_TARGET_DATE).GetDateTime();
        }

        public void SetEntryTargetDate(int id, DateTime date)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            _writer.Write(new ShortDate(date), (int)GetEntryDataOffset(id) + OFFSET_ENTRY_TARGET_DATE);
        }

        public DateTime GetEntryCreatedDate(int id)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            return _reader.ReadShortDate(GetEntryDataOffset(id) + OFFSET_ENTRY_CREATED_DATE).GetDateTime();
        }

        public void SetEntryCreatedDate(int id, DateTime date)
        {
            Contract.Requires(id >= 0 && id < Capacity);

            _writer.Write(new ShortDate(date), (int)GetEntryDataOffset(id) + OFFSET_ENTRY_CREATED_DATE);
        }

        #endregion // Public Methods
    }
}
